#include <hidl/HidlTransportSupport.h>
#include <log/log.h>

#include <vendor/ecarx/xma/audiocontrol/1.0/BpHwEcarxAudioControlXcallCallback.h>

#include "EcarxAudioControl.h"

namespace vendor {
namespace ecarx {
namespace xma {
namespace audiocontrol {
namespace V1_0 {
namespace impl {

McuControl* EcarxAudioControl::sMcuControl = nullptr;
EcarxAudioControl* EcarxAudioControl::sInstance = nullptr;

EcarxAudioControl::EcarxAudioControl() {
    ALOGD("%s", __FUNCTION__);
    sMcuControl = McuControl::getInstance();
    int ret = sMcuControl->setupConnection(AUDIO_SERVICEID_SECOND);
    if (ret != 0) {
        ALOGE("mcu connection setup failed, err:%d", ret);
    }
    sMcuControl->setCallback(mcuCallback);
    //sMcuControl->setCallback(std::bind(&EcarxAudioControl::mcuCallback, this, std::placeholders::_1));

    mCallbackDeathRecipient = new DeathRecipient(
                    std::bind(&EcarxAudioControl::onCallbackDead, this, std::placeholders::_1));
    sInstance = this;
}

EcarxAudioControl::~EcarxAudioControl() {

}

void EcarxAudioControl::mcuCallback(status_t status) {
    ALOGD("%s Callback data:0x%x", __FUNCTION__, status);
    sInstance->notifyHalClients(static_cast<uint8_t>(status));
}

void EcarxAudioControl::notifyHalClients(uint8_t status) {
    ALOGD("%s status:0x%x", __FUNCTION__, status);
    {
        MuxGuard g(mLock);
        for (auto it=mClients.begin(); it!=mClients.end(); ++it) {
            ALOGD("notify hal client");
            it->second->getCallback()->onXcallStatus((status));
        }
    }
}

void EcarxAudioControl::mcuErrorCallbacl(status_t err) {
    ALOGD("%s errorno:%d", __FUNCTION__, err);
}

Return<void> EcarxAudioControl::setAudioSource(AudioSourceType source) {
    ALOGD("%s source type:%d", __FUNCTION__, source);
    sMcuControl->setAudioSource(static_cast<int>(source));
    return Void();
}

Return<void> EcarxAudioControl::setMuteOnOff(SourceType channel, OnOff1 status) {
    ALOGD("%s channel:%d status:%d", __FUNCTION__, channel, status);
    sMcuControl->setMuteOnOff(static_cast<int>(channel), static_cast<int>(status));
    return Void();
}

Return<void> EcarxAudioControl::adjustVolume(SourceType channel, uint32_t step) {
    ALOGD("%s channel:%d step:%d", __FUNCTION__, channel, step);
    sMcuControl->adjustVolume(static_cast<int>(channel), static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setFader(uint32_t step) {
    ALOGD("%s step:%d", __FUNCTION__, step);
    sMcuControl->setFader(static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setBalance(uint32_t step) {
    ALOGD("%s step:%d", __FUNCTION__, step);
    sMcuControl->setBalance(static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setFaderBalanceDirect(uint32_t fader, uint32_t balance) {
    ALOGD("%s fader:%d balance:%d", __FUNCTION__, fader, balance);
    sMcuControl->setFaderBalanceDirect(static_cast<int>(fader), static_cast<int>(balance));
    return Void();
}

Return<void> EcarxAudioControl::setBass(uint32_t step) {
    ALOGD("%s step:%d", __FUNCTION__, step);
    sMcuControl->setBass(static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setMiddle(uint32_t step) {
    ALOGD("%s step:%d", __FUNCTION__, step);
    sMcuControl->setMiddle(static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setTreble(uint32_t step) {
    ALOGD("%s step:%d", __FUNCTION__, step);
    sMcuControl->setTreble(static_cast<int>(step));
    return Void();
}

Return<void> EcarxAudioControl::setEqualizerMode(EqType mode) {
    ALOGD("%s mode:%d", __FUNCTION__, mode);
    sMcuControl->setEqualizerMode(static_cast<int>(mode));
    return Void();
}

Return<void> EcarxAudioControl::setLoudness(OnOff2 loudness) {
    ALOGD("%s loudness:%d", __FUNCTION__, loudness);
    sMcuControl->setLoudness(static_cast<int>(loudness));
    return Void();
}

Return<void> EcarxAudioControl::mixSource(MixType source, MixStatus status) {
    ALOGD("%s source:%d status:%d", __FUNCTION__, source, status);
    sMcuControl->mixSource(static_cast<int>(source), static_cast<int>(status));
    return Void();
}

Return<void> EcarxAudioControl::audioSettingRecovery() {
    ALOGD("%s", __FUNCTION__);
    sMcuControl->audioSettingRecovery();
    return Void();
}

Return<void> EcarxAudioControl::setSeatOptimization(SeatType seat) {
    ALOGD("%s seat:%d", __FUNCTION__, seat);
    sMcuControl->setSeatOptimization(static_cast<int>(seat));
    return Void();
}

Return<void> EcarxAudioControl::setVehicleSpeedLevel(VslType level) {
    ALOGD("%s level:%d", __FUNCTION__, level);
    sMcuControl->setVehicleSpeedLevel(static_cast<int>(level));
    return Void();
}

Return<void> EcarxAudioControl::setClariFi(OnOff1 status) {
    ALOGD("%s status:%d", __FUNCTION__, status);
    sMcuControl->setClariFi(static_cast<int>(status));
    return Void();
}

Return<void> EcarxAudioControl::setLogicalSeven(OnOff1 status) {
    ALOGD("%s status:%d", __FUNCTION__, status);
    sMcuControl->setLogicalSeven(static_cast<int>(status));
    return Void();
}

Return<void> EcarxAudioControl::setBoseRoomMode(BoseMode mode) {
    ALOGD("%s mode:%d", __FUNCTION__, mode);
    sMcuControl->setBoseRoomMode(static_cast<int>(mode));
    return Void();
}

Return<void> EcarxAudioControl::setCAEOn(CAEOn isOn) {
    ALOGD("%s CAE isOn:%d", __FUNCTION__, isOn);
    sMcuControl->setCAEOn(static_cast<int>(isOn));
    return Void();
}

Return<void> EcarxAudioControl::setMixMode(MixMode mode) {
    ALOGD("%s mixmode:%d", __FUNCTION__, mode);
    sMcuControl->setMixMode(static_cast<int>(mode));
    return Void();
}

Return<void> EcarxAudioControl::setKTVOnOff(KTVOn on) {
    ALOGD("%s KTV isOn:%d", __FUNCTION__, on);
    sMcuControl->setKTVOnOff(static_cast<int>(on));
    return Void();
}

Return<void> EcarxAudioControl::setSoundEnhance(SoundEnhance type) {
    ALOGD("%s setSoundEnhance type:%d", __FUNCTION__, type);
    sMcuControl->setSoundEnhance(static_cast<int>(type));
    return Void();
}

Return<void> EcarxAudioControl::setOperationPosition(OperaPosition position) {
    ALOGD("%s setOperationPosition position:%d", __FUNCTION__, position);
    sMcuControl->setOperaPosition(static_cast<int>(position));
    return Void();
}

Return<void> EcarxAudioControl::setHXTOnOff(HXTOn onOff) {
    ALOGD("%s setHXTOnOff onOff:%d", __FUNCTION__, onOff);
    sMcuControl->setHXTOnOff(static_cast<int>(onOff));
    return Void();
}


Return<void> EcarxAudioControl::setRNCOnOff(RNCOn rncOnOff) {
    ALOGD("%s setRNCOnOff rncOnOff:%d", __FUNCTION__, rncOnOff);
    sMcuControl->setRNCOnOff(static_cast<int>(rncOnOff));
    return Void();
}

Return<void> EcarxAudioControl::setPartitionAudio(PartitionAudio partion) {
    ALOGD("%s setPartitionAudio partion:%d", __FUNCTION__, partion);
    sMcuControl->setPartitionAudio(static_cast<int>(partion));
    return Void();
}


Return<void> EcarxAudioControl::setICCOnOff(ICCOn iccOnOff) {
    ALOGD("%s setICCOnOff iccOnOff:%d", __FUNCTION__, iccOnOff);
    sMcuControl->setICCOnOff(static_cast<int>(iccOnOff));
    return Void();
}

Return<void> EcarxAudioControl::setWarnVolLevl(WarnVolLevel WarnVolLevel) {
    ALOGD("%s setWarnVolLevl:%d", __FUNCTION__, WarnVolLevel);
    sMcuControl->setWarnVolLevl(static_cast<int>(WarnVolLevel));
    return Void();
}

Return<void> EcarxAudioControl::setHighEQ(HighEQ higheq) {
    ALOGD("%s setHighEQ(HighEQ higheq):%d", __FUNCTION__, higheq);
    sMcuControl->setHighEQ(static_cast<int>(higheq));
    return Void();
}


Return<void> EcarxAudioControl::setMaxBassLvl(MaxBassLevel maxBassLvl) {
    ALOGD("%s setMaxBassLvl:%d", __FUNCTION__, maxBassLvl);
    sMcuControl->setMaxBassLvl(static_cast<int>(maxBassLvl));
    return Void();
}


Return<void> EcarxAudioControl::setTwoSurround(TwoSurround twosurround) {
    ALOGD("%s setTwoSurround:%d", __FUNCTION__, twosurround);
    sMcuControl->setTwoSurround(static_cast<int>(twosurround));
    return Void();
}

Return<void> EcarxAudioControl::setTwoSurroundLvl(TwoSurroundLvl twosurroundLvl) {
    ALOGD("%s setTwoSurroundLvl:%d", __FUNCTION__, twosurroundLvl);
    sMcuControl->setTwoSurroundLvl(static_cast<int>(twosurroundLvl));
    return Void();
}

Return<void> EcarxAudioControl::setThreeSurround(ThreeSurround threesurround) {
    ALOGD("%s setThreeSurround:%d", __FUNCTION__, threesurround);
    sMcuControl->setThreeSurround(static_cast<int>(threesurround));
    return Void();
}

Return<void> EcarxAudioControl::setThreeSurroundLvl(ThreeSurroundLvl threesurroundLvl) {
    ALOGD("%s setThreeSurroundLvl:%d", __FUNCTION__, threesurroundLvl);
    sMcuControl->setThreeSurroundLvl(static_cast<int>(threesurroundLvl));
    return Void();
}

Return<void> EcarxAudioControl::setSourceRear(SourceRear sourceRear) {
    ALOGD("%s setSourceRear:%d", __FUNCTION__, sourceRear);
    sMcuControl->setSourceRear(static_cast<int>(sourceRear));
    return Void();
}

Return<void> EcarxAudioControl::setDuckAudioPolicy(DuckMode focusChangeHint, uint32_t db ,DuckSource source){
    ALOGD("%s setDuckAudioPolicy: DuckMode:%d,db:%d,DuckSource:%d", __FUNCTION__, focusChangeHint,db,source);
    //sMcuControl->setDuckAudioPolicy(static_cast<int>(focusChangeHint),static_cast<int>(db),static_cast<int>(source));
    return Void();
}

Return<void> EcarxAudioControl::setSoundZone(uint32_t zone) {
    ALOGD("%s setSoundZone:%d", __FUNCTION__, zone);
    sMcuControl->setSoundZone(zone);
    return Void();
}


Return<void> EcarxAudioControl::getAudioStatus(getAudioStatus_cb _hidl_cb) {
    ALOGD("%s", __FUNCTION__);
    int data[40];
    size_t len = 0;
    sMcuControl->getAudioStatus(data, &len);
    ALOGD("len:%zu", len);
    hidl_vec<uint8_t> result(len);
    for(int i=0; i<(int)len; i++) {
        result[i] = static_cast<uint8_t>(data[i]);
    }
    _hidl_cb(result);
    return Void();
}

Return<void> EcarxAudioControl::getXCallActive(XcallType type, XcallStatus status) {
    ALOGD("%s type:%d status:%d", __FUNCTION__, type, status);
    sMcuControl->getXCallActive(static_cast<int>(type), static_cast<int>(status));
    return Void();
}

Return<int32_t> EcarxAudioControl::registerXcallStatusCallback(const sp<IEcarxAudioControlXcallCallback>& callback) {
    ALOGD("%s", __FUNCTION__);
    ClientId clientId = getClientId(callback);
    {
        MuxGuard g(mLock);
        auto it = mClients.find(clientId);
        if (it == mClients.end()) {
            uint64_t cookie = reinterpret_cast<uint64_t>(clientId);
            ALOGI("Creating new client and linking to death recipient, cookie: 0x%lx", cookie);
            auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
            if (!res.isOk()) {  // Client is already dead?
                ALOGW("%s failed to link to death, client %p, err: %s",
                      __func__, callback.get(), res.description().c_str());
                return -1;
            }
            sp<HalClient> client = new HalClient(callback);
            mClients.insert({clientId, client});
        } else {
            ALOGW("callback client already been registered");
        }
    }
    return 0;
}

Return<void> EcarxAudioControl::unregisterXcallStatusCallback(const sp<IEcarxAudioControlXcallCallback>& callback) {
    ALOGD("%s", __FUNCTION__);
    ClientId clientId = getClientId(callback);
    {
        MuxGuard g(mLock);
        auto it = mClients.find(clientId);
        if (it == mClients.end()) {
            ALOGW("callback client was not registered");
        } else {
            removeClientLocked(clientId);
        }
    }
    return Void();
}

void EcarxAudioControl::removeClientLocked(ClientId clientId) {
    ALOGD("%s", __FUNCTION__);
    auto clientIter = mClients.find(clientId);
    const auto& halClient = clientIter->second;
    auto res = halClient->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
    if (!res.isOk()) {
        ALOGW("%s failed to unlink to death, client: %p, err: %s",
                __func__, halClient->getCallback().get(), res.description().c_str());
    }
    mClients.erase(clientIter);
    return;
}

ClientId EcarxAudioControl::getClientId(const sp<IEcarxAudioControlXcallCallback> callback) {
    // TODO rework this to get some kind of unique id for callback interface when this
    // feature is ready in HIDL.
    if (callback->isRemote()) {
        BpHwEcarxAudioControlXcallCallback* hwCallback =
                static_cast<BpHwEcarxAudioControlXcallCallback*>(callback.get());
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder()));
    } else {
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get()));
    }
}

void EcarxAudioControl::onCallbackDead(uint64_t cookie) {
    ALOGD("%s cookie:0x%lx", __FUNCTION__, cookie);
    ClientId clientId = cookie;
    {
        MuxGuard g(mLock);
        const auto& it = mClients.find(clientId);
        if (it == mClients.end()) {
            return;
        }
        removeClientLocked(clientId);
    }
}

}  // namespace impl
}  // namespace V1_0
}  // namespace audiocontrol
}  // namespace xma
}  // namespace ecarx
}  // namespace vendor
